package be.ac.ua.comp.projectGPT.players;
import be.ac.ua.comp.projectGPT.Jobs.Job;
import be.ac.ua.comp.projectGPT.biddingAgents.BiddingAgent;
import be.ac.ua.comp.projectGPT.exceptions.DuplicateException;
import be.ac.ua.comp.projectGPT.exceptions.InsufficientFundsException;
import be.ac.ua.comp.projectGPT.exceptions.InsufficientJobsException;
import be.ac.ua.comp.projectGPT.exceptions.NonExistingAccountException;
import java.util.HashMap;
import java.util.Observable;
import java.util.Observer;
import java.util.Vector;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Consumer extends Person implements Observer {

        Bank bank;
	long budget;
	LinkedBlockingQueue<Job> jobs;
	HashMap<BiddingAgent,Provider> biddingAgents;
        Vector<Long> fiveLastAccepted = new Vector<Long>();
        int wonAuctions = 0;
        int lostAuctions = 0;
        int participations = 0;
        long medianWinningBid = 0;
        long medianAcceptedBid = 0;
        
    public void setBank(Bank bank) {
        this.bank = bank;
    }

    public void setProvider(BiddingAgent biddingAgent,Provider provider) {
        biddingAgents.put(biddingAgent, provider);
    }

        public Consumer(String name,long budget,Bank bank) throws DuplicateException {
            super(name);
            jobs = new LinkedBlockingQueue<Job>();
            biddingAgents = new HashMap<BiddingAgent,Provider>();
            this.budget = budget;
            this.bank = bank;
            bank.addAccount(this, budget);
        }
	
	public void addJob(Job job) {
            logger.info("Adding job "+job.getId());
            jobs.add(job);
	}

        public void win(Provider provider,long amount) {
        try {
            logger.info("I won!");
            fiveLastAccepted.add(amount);
            wonAuctions++;
            logger.info("Transferring "+amount+" from "+this.getName()+" to "+provider.getName());
            bank.transfer(this, provider, amount);
            Job job = jobs.poll();
            logger.info("Sending off job " + job.getId());
            provider.setJob(this, job);
        } catch (NonExistingAccountException ex) {
            logger.info("NonExistingAccountException: "+ex.getLocalizedMessage());
        } catch (InsufficientFundsException ex) {
            logger.info("InsufficientFundsException: "+ex.getLocalizedMessage());
        }
        }

        public void lose() {
            lostAuctions++;
        }

        public void printStatistics() {
            logger.info("---");
            logger.info("Last 5 accepted bids: "+fiveLastAccepted.toString());
            logger.info("Won auctions: "+wonAuctions);
            logger.info("Lost auctions: "+lostAuctions);
            logger.info("Participations: "+participations);
            logger.info("Median winning bid: "+medianWinningBid);
            logger.info("Median accepted bid: "+medianAcceptedBid);
            logger.info("---");
        }

        public long askBudget() {
            if(!bank.available(this, 200)) {
                return 0;
            } else {
                            participations++;
            return 200;
            }
            
        }
	
	public void withdrawMoney(long money) {
            try {
                logger.info("Withdrawing "+money);
		bank.withdraw(this, money);
            } catch(InsufficientFundsException ex) {
                logger.error("InsufficientFundsException: "+ex.getLocalizedMessage());
            } catch(NonExistingAccountException ex) {
                logger.error("NonExistingAccountException: "+ex.getLocalizedMessage());
            }
	}
	
	public boolean hasMoney(int money) {
		return bank.available(this, money);
	}
	
	public boolean hasJobs() {
		return (jobs.size() > 0);
	}
	
	public Job checkoutJob() throws InsufficientJobsException {
            if(!hasJobs()) {
		throw new InsufficientJobsException();
            }
            logger.info("Checking out job");
            return jobs.poll();
	}
	
	public void removeBiddingAgent(BiddingAgent biddingAgent) {
            if(biddingAgents.containsKey(biddingAgent)) {
                logger.info("Removing bidding agent "+biddingAgent.getName());
		biddingAgents.remove(biddingAgent);
            }
	}

    public void update(Observable o, Object arg) {
        logger.info("Job finished: "+((Job) o).getId() + " with status "+ (String)arg);
    }



}

